<?php

namespace MicroCyan\WechatRev\Kernel;
use GuzzleHttp\Client;

class App
{

    private $http = null;
    private $tokenKey = null;
    private $tokenKeyExpire = 3600*24;
    function __construct(){
        $base_uri = config('mc.wechat_rev.url','');
        $this->http = new Client(['base_uri'=>$base_uri]);
        $this->tokenKey = config('mc.wechat_rev.tokenKey','wechat:reverse');
        $this->tokenKeyExpire = config('mc.wechat_rev.tokenKeyExpire',3600*24);
    }

    public function setBindKey($ChatRoomKey): string
    {
        $token = 'MC_'.mt_rand(1000000,9999999);
        $bindKey = config('mc.wechat_rev.bindKey','wechat:bindGroup:').$token;
        $bindKeyExpire = config('mc.wechat_rev.bindKeyExpire',600);
        cache($bindKey,$ChatRoomKey,['expire'=>$bindKeyExpire]);
        return $token;
    }
    public function getBindValue($token){
        if (is_string($token)&&preg_match('/(MC_\d{7})/',$token,$matches)&&!empty($matches[1])){
            $bindKey = config('mc.wechat_rev.bindKey','wechat:bindGroup:').$matches[1];
            $ChatRoomKey = cache($bindKey)??'';
            cache($bindKey,null);
        }
        return $ChatRoomKey??'';
    }
    public function getWxProfile($wxid){
        $rsp = $this->http_post('/api/WxProfile/Profile',[],true,$wxid,[
            'Content-Type'=>'application/json',
            'Authorization'=>$this->getToken()
        ]);
        return array_merge(
            array_by_field($rsp['obj']['userInfo']['userName']??[],'string as userName'),
            array_by_field($rsp['obj']['userInfo']['nickName']??[],'string as nickName'),
            array_by_field($rsp['obj']['userInfo']['bindMobile']??[],'string as bindMobile'),
            array_by_field($rsp['obj']['userInfo']??[],'sex,province,city,signature,alias,country'),
            array_by_field($rsp['obj']['userInfoExt']??[],'smallHeadImgUrl as avatar')
        );
    }
    public function getMessageSync($wxid): array
    {
        $rsp = $this->http_post('/api/WxSync/Sync',[],true,$wxid,[
            'Content-Type'=>'application/json',
            'Authorization'=>$this->getToken()
        ]);
        $this->setSyncSessionKey($wxid,$rsp['obj']['syncKey']??'');
        if (!empty($rsp['obj']['array'])&&is_array($rsp['obj']['array'])){
            return array_map(function ($i){
                $i['info'] = json_decode($i['info']??'{}',true);
                return $i;
            },$rsp['obj']['array']);
        }
        return [];
    }
    public function getSnsPage($wxid,$target_wxid,$type=1001,$maxId=0){
        $rsp = $this->http_post('/api/WxSns/SnsPage',[ 'wxId'=>$target_wxid, 'maxId'=>$maxId, 'type'=>$type ],true,$wxid,[
            'Content-Type'=>'application/json',
            'Authorization'=>$this->getToken()
        ]);
        if (!isset($rsp['obj']['array'])){
            return [];
        }
        $rsp['obj']['array']= array_map(function ($sns){
            $sns['snsinfo'] = simplexml_load_string($sns['snsinfo']??'','SimpleXMLElement',LIBXML_NOCDATA);
            $sns['snsinfo'] = json_decode(json_encode($sns['snsinfo']??'',JSON_UNESCAPED_UNICODE),true);
            return $sns;
        },$rsp['obj']['array']??[]);
        return $rsp['obj'];
    }
    public function getChatRoomMembers($wxid,$chatRoomId){
        $rsp = $this->http_post('/api/WxChatRoom/RoomMemberDetail',[
            'id'   =>$chatRoomId,
            'clientVersion'=>0
        ],true,$wxid,[
            'Content-Type'=>'application/json',
            'Authorization'=>$this->getToken()
        ]);
        return $rsp['obj']['newChatroomData']['chatRoomMember']??[];
    }
    private function getToken():string
    {
        $token = cache($this->tokenKey);
        if (!empty($token)){
            return $token;
        }
        $username = config('mc.wechat_rev.username','');
        $password = config('mc.wechat_rev.password','');
        $rsp = $this->http_get('/Login',[ 'userName'=>$username, 'passWord'=>$password ]);
        if (!empty($rsp['obj'])){
            $token = 'Bearer '.$rsp['obj'];
            cache($this->tokenKey,$token,['expire'=>$this->tokenKeyExpire]);
            return $token;
        }
        throw new \Exception('反编译Token获取失败');
    }
    private function getUser($wxid){
        if (empty($wxid)){
            return [];
        }
        $userName = 'not_define';
        if (!empty($wxid)&&is_string($wxid)){
            if (strlen($wxid) < 32){
                $robot = model('mc_wechat_rev_robot_lists')->where(['userName'=>$wxid])->cache(true,17)->find();
                $userName = $robot['userName']??'';
                $token    = $robot['token']??'';
            }else{
                $userName = 'with_token';
                $token = $wxid;
            }
        }
        if ( !empty($token = json_decode($token??'{}',true)) && !empty($token['uin']) ){
            $token['syncKey'] = $this->getSyncSessionKey($userName);
            return $token;
        }
        throw new \Exception('获取RobotSession失败:'.$userName);
    }
    private function getProxy(): array
    {
        $proxys = model('mc_wechat_rev_robot_ips')->where(['status'=>1,'del_time'=>0])->cache(true,13)->column('proxy');
        if (empty($proxys)){
            throw new \Exception('暂无可用的代理配置');
        }
        $proxy = $proxys[array_rand($proxys)];
        $deployArr = explode(':',$proxy);
        if (!empty($deployArr)&&count($deployArr)===4){
            return [
                "proxyIp"  => $deployArr[0],
                "proxyPort"=> $deployArr[1],
                "proxyUser"=> $deployArr[2],
                "proxyPass"=> $deployArr[3],
                "proxyType"=> 0
            ];
        }
        throw new \Exception('无效的代理配置：'.$proxy);
    }
    private function getBody($body,$wxid=false,$isProxy=false){
        if (!is_array($body)){
            $body = json_decode($body??'{}',true);
        }
        if ($isProxy){
            $body['proxy'] = $this->getProxy();
        }
        $body['user']  = $this->getUser($wxid);
        return json_encode($body,JSON_UNESCAPED_UNICODE);
    }
    private function getHeaders($headers){
        $rev_headers = config('mc.wechat_rev.headers');
        if (empty($rev_headers) || !is_array($rev_headers)){
            $rev_headers = [];
        }
        return array_merge($rev_headers,$headers);
    }
    private function http_get($path,$query=[],$headers=[]){
        $options = [];
        $options['headers'] = $this->getHeaders($headers);
        $options['query']   = $query;
        $rsp = $this->http->request('GET', $path,$options)->getBody()->getContents();
        return json_decode($rsp??'{}',true)??[];
    }
    private function http_post($path,$body=[],$isProxy=false,$wxid=false,$headers=[]){
        $options = [];
        $options['headers'] = $this->getHeaders($headers);
        $options['body']    = $this->getBody($body,$wxid,$isProxy);
        try {
            $rsp = $this->http->request('POST', $path,$options)->getBody()->getContents();
            if (strpos($rsp,'session error')!==false){
                model('mc_wechat_rev_robot_lists')->where(['userName'=>$wxid])->save(['status'=>2]);
            }
            return json_decode($rsp??'{}',true)??[];
        }catch (\Exception $exception){
            if (strpos($exception->getMessage(),'401 Unauthorized')!==false){
                cache($this->tokenKey,null);
            }else if (strpos($exception->getMessage(),'500 Internal Server Error')!==false){
                if (!empty($proxy = json_decode(is_string($options['body'])?$options['body']:'',true)['proxy']['proxyIp']??'')){
                    model('mc_wechat_rev_robot_ips')->where('proxy','like',$proxy.'%')->save(['status'=>2]);
                }
            }
            throw $exception;
        }
    }
    private function getSyncSessionKey($wxid){
        $syncKey = config('mc.wechat_rev.syncKey','wechat:syncKey:').$wxid;
        return cache($syncKey)??'';
    }
    private function setSyncSessionKey($wxid,$value):bool
    {
        if (!empty($value)){
            $syncKey = config('mc.wechat_rev.syncKey','wechat:syncKey:').$wxid;
            $syncKeyExpire = config('mc.wechat_rev.syncKeyExpire',3600);
            return cache($syncKey,$value,['expire'=>$syncKeyExpire]);
        }else{
            return false;
        }
    }

}